Spring filter原理深入浅出

Spring filter原理深入浅出

在这篇文章中,我会介绍一个请求从浏览器进来,是如何被Spring拦截到的,Spring又是如何处理这个请求的,filter有什么作用,filter的原理等

一、前置知识

OK,我们首先介绍一下,请求从浏览器过来,我们的服务器会经历些什么。先看一张jetty的内部结构图

jetty内部组成

我们看到jetty的核心组件包括了一大堆的*Handler ,对于这些handler,不论任何容器基本都有,可能名称不太相同,笔者这里以undertow这个容器为例,浏览器发送过来的请求,会经过一个FilterHandler 这个handler跟我们今天的主题相关。

二、Request阶段

先看一下这个handler的处理代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void handleRequest(HttpServerExchange exchange) throws Exception {
ServletRequestContext servletRequestContext = (ServletRequestContext)exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
ServletRequest request = servletRequestContext.getServletRequest();
ServletResponse response = servletRequestContext.getServletResponse();
DispatcherType dispatcher = servletRequestContext.getDispatcherType();
Boolean supported = (Boolean)this.asyncSupported.get(dispatcher);
if (supported != null && !supported.booleanValue()) {
servletRequestContext.setAsyncSupported(false);
}
List<ManagedFilter> filters = (List)this.filters.get(dispatcher);
if (filters == null) {
this.next.handleRequest(exchange);
} else {
FilterHandler.FilterChainImpl filterChain = new FilterHandler.FilterChainImpl(exchange, filters, this.next, this.allowNonStandardWrappers);
filterChain.doFilter(request, response);
}
}

前面都是拿request和response我们不用看,主要是看后面几行代码,filters 如果我们有配置自己的filter,这里肯定不会为空,那么就走下面的filter 链 ,我们看下FilterHandler里面的doFilter的代码

1
2
3
4
5
6
int index = this.location++;
if (index >= this.filters.size()) {
this.next.handleRequest(this.exchange);
} else {
((ManagedFilter)this.filters.get(index)).doFilter(request, response, this);
}

​ location默认是0,所以会从list里面的第一个元素取起,依次执行,OK,我们知道,filter里面一般会有一个filterChain.doFilter(request, response); 这句话,代表filter链继续执行,这时候,这里的location就会+1 ,变成执行第二个,第三个,链条就转动起来了。终于执行到我们自己写的ResponseWrapperFilter ,这个filter大家可以在我后面的源码里看到,这里我先说有这么一个自己写的filter,我用来包装我们的response,给response做些手脚。哈哈。

​ OK,我们继续,当所有的filter都执行完毕之后,就会开始执行this.next.handleRequest(exchange); 这段代码,也就是在继续执行我们刚才的handler链,next表示的是ServletHandler` 这个类,执行代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
servlet = this.managedServlet.getServlet();
((Servlet)servlet.getInstance()).service(request, response);
} catch (UnavailableException var12) {
if (var12.isPermanent()) {
UndertowServletLogger.REQUEST_LOGGER.stoppingServletDueToPermanentUnavailability(this.managedServlet.getServletInfo().getName(), var12);
this.managedServlet.stop();
this.managedServlet.setPermanentlyUnavailable(true);
exchange.setStatusCode(404);
} else {
unavailableUntilUpdater.set(this, System.currentTimeMillis() + (long)(var12.getUnavailableSeconds() * 1000));
UndertowServletLogger.REQUEST_LOGGER.stoppingServletUntilDueToTemporaryUnavailability(this.managedServlet.getServletInfo().getName(), new Date(until), var12);
exchange.setStatusCode(503);
}

一旦掉用了下面这段代码,我们的处理逻辑就会被调用,说白了就是我们Controller里面的那些逻辑就会被调用

1
((Servlet)servlet.getInstance()).service(request, response);

这段代码很重要

三、reponse阶段

​ 经过了上面这段代码的执行,我们的filter开始收拢了,所有的filter在执行filterChain.doFilter(request, response);这段代码之后其实代码是还没有执行完的,如果你下面还有写代码的话,大家好好品味一下这句话,是不是这个意思。因为每个filter都去调用下一个filter了,等最后一个filter执行完毕之后,就开始慢慢收拢,这时候我们的reponse也有了。

​ 当所有的filter执行完毕自己的后置代码之后,就回到了我们最初的地方,我们的最初的FilterHandler开始调用的地方。undertow服务器就继续开始执行了,它就会执行下面的代码输出你想要的信息

1
2
3
4
if (!exchange.isDispatched() && !(exchange.getConnection() instanceof ServletInitialHandler.MockServerConnection)) {
servletRequestContext.getOriginalResponse().responseDone();
servletRequestContext.getOriginalRequest().clearAttributes();
}

主要就是把输出流写入到socket中,然后客户端接收

好啦,今天就讲到这里啦,这文章还没有结束,请期待后续 ^ _ ^

Share